/* 
 * shamefully copy from AnFetion.
 * more see: http://code.google.com/p/anfetion/
 */
package person.bangbang.im.Androidgin.Fetion;

import java.util.ArrayList;
import java.util.HashSet;

import person.bangbang.im.Androidgin.R;
import person.bangbang.im.Androidgin.Fetion.TcpSendAndReceive.IPackageReceivedHandler;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import android.content.Context;
import android.media.MediaPlayer;


public class ReserveNotify extends SIPCBase implements IPackageReceivedHandler {

    private FetionUri fetionid;
    private ArrayList<FetionFriend> buddies;
    private Context ctx;

    private HashSet<IFriendInfoChanged> _friendInfoChangedListener = new HashSet<IFriendInfoChanged>();

    public ReserveNotify(/*Context ctx, bangbang.song@gmail.com*/FetionUri fetionid, ArrayList<FetionFriend> friends,
            TcpSendAndReceive connection, SerialManager serialManager) {
        super(serialManager, connection);
        this.fetionid = fetionid;
        this.buddies = friends;
//        this.ctx = ctx;
    }

    public ArrayList<FetionFriend> getBuddies() {
        return buddies;
    }

    @Override
    public boolean execute() {
        // 定制接收通知包
        connection.addPackageReceivedQueue(this);
        ArrayList<FetionSIPC.HeaderPair> headers = new ArrayList<FetionSIPC.HeaderPair>();
        headers.add(new FetionSIPC.HeaderPair("F", this.fetionid.getId()));
        headers.add(new FetionSIPC.HeaderPair("I", serialManager.getISerial() + " "));
        headers.add(new FetionSIPC.HeaderPair("Q", this.serialManager.getSSerial() + " S"));
        headers.add(new FetionSIPC.HeaderPair("N", "GetContactsInfo"));
        StringBuilder body = new StringBuilder("<args><contacts attributes=\"provisioning;impresa;mobile-no;")
                .append("nickname;name;portrait-crc;ivr-enabled\" extended-attributes=\"score-level\">");
        for (int i = 0; i < this.buddies.size(); i++) {
            if (!this.buddies.get(i).IsMobileNo) {
                body.append("<contact uri=\"").append(this.buddies.get(i).Uri).append("\" />");
            }
        }
        body.append("</contacts></args>");
        headers.add(new FetionSIPC.HeaderPair("L", StrUtil.getLength(body)));

        FetionSIPC sipc = new FetionSIPC(SIPC_COMMON_HEADER, headers, body.toString());
        if (this.SendAndReceiveResponse(sipc)) {
            try {
                if (!sipc.response.getResponseCmdLine().code.equals(SIPC_RESPONSE_CODE_OK)) {
                    this.onSetText("Get friends' info fail");
                    return false;
                } else {
                    Document dom = parseXmlDocument(sipc.response.body);
                    if (dom != null) {
                        NodeList contacts = dom.getDocumentElement().getElementsByTagName("contact");
                        this.updateFriendInfo(contacts);
                    } else return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                this.onSetText("Get friends' info fail");
                return false;
            }
        } else {
            this.onSetText("Time out while getting friends info.");
            return false;
        }
        headers = new ArrayList<FetionSIPC.HeaderPair>();

        headers.add(new FetionSIPC.HeaderPair("F", this.fetionid.getId()));
        headers.add(new FetionSIPC.HeaderPair("I", serialManager.getISerial() + " "));
        headers.add(new FetionSIPC.HeaderPair("Q", String.valueOf(serialManager.getSubSerial()) + " SUB"));
        headers.add(new FetionSIPC.HeaderPair("N", "presence"));
        body = new StringBuilder("<args><subscription><contacts>");
        int size = buddies.size();
        for (int i = 0; i < size; i++) {
            if (!this.buddies.get(i).IsMobileNo) {
                body.append("<contact uri=\"").append(this.buddies.get(i).Uri).append("\" />");
            }
        }
        body
                .append("</contacts><presence><basic attributes=\"all\" />"
                        + "<personal attributes=\"all\" /><extended types=\"sms;location;listening;ring-back-tone\" />"
                        + "</presence></subscription></args>");
        headers.add(new FetionSIPC.HeaderPair("L", StrUtil.getLength(body)));

        sipc = new FetionSIPC("SUB fetion.com.cn SIP-C/2.0", headers, body.toString());
        if (this.SendAndReceiveResponse(sipc)) {
            try {
                if (sipc.response.getResponseCmdLine().code.equals(SIPC_RESPONSE_CODE_OK)) {
                    return true;
                } else {
                    this.onSetText("Customize friends' status fail");
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                this.onSetText("Customize friends' status fail");
                return false;
            }
        } else {
            this.onSetText("Time out while getting friends info.");
            return false;
        }
    }

    public void addFriendInfoChangedListener(IFriendInfoChanged obj) {
        this._friendInfoChangedListener.add(obj);
    }

    public void removeFriendInfoChangedListener(IFriendInfoChanged obj) {
        this._friendInfoChangedListener.remove(obj);
    }

    private void onFriendInfoChanged(FetionFriend obj) {
        for (IFriendInfoChanged iic : _friendInfoChangedListener) {
            iic.Happened(obj);
        }
    }

    private void updateFriendInfo(NodeList contacts) {
        for (int i = 0; i < contacts.getLength(); i++) {
            Node statuscode = contacts.item(i).getAttributes().getNamedItem("status-code");
            if (statuscode != null && statuscode.getNodeValue().equals(SIPC_RESPONSE_CODE_OK)) {
                String frienduri = contacts.item(i).getAttributes().getNamedItem("uri").getNodeValue();
                int size = this.buddies.size();
                for (int j = 0; j < size; j++) {
                    FetionFriend ff = this.buddies.get(j);
                    if (frienduri.equals(ff.Uri)) {
                        NodeList childs = contacts.item(i).getChildNodes();
                        for (int k = 0; k < childs.getLength(); k++) {
                            Element n = (Element) childs.item(k);
                            if (n.getNodeName().equals("personal")) {
                                if (n.getAttributeNode("nickname") != null) {
                                    ff.setNickName(n.getAttributeNode("nickname").getNodeValue());
                                }
                                if (n.getAttributeNode("mobile-no") != null) {
                                    ff.mobile_no = n.getAttributeNode("mobile-no").getNodeValue();
                                }
                                if (n.getAttributeNode("impresa") != null) {
                                    ff.impresa = n.getAttributeNode("impresa").getNodeValue();
                                }
                                this.onFriendInfoChanged(ff);
                            }
                        }
                        break;
                    }
                }
            } else if (statuscode != null && statuscode.getNodeValue().equals(SIPC_RESPONSE_CODE_NOTEXIST)) {
                String frienduri = contacts.item(i).getAttributes().getNamedItem("uri").getNodeValue();
                if (MiscUtil.removeFriend(frienduri, buddies)) {
                    this.onFriendInfoChanged(null);
                }
            }
        }
    }

    private void updateFriendPresence(Element event) {
        NodeList presences = event.getElementsByTagName("presence");

        for (int i1 = 0; i1 < presences.getLength(); i1++) {
            if (!(presences.item(i1) instanceof Element)) {
                continue;
            }
            Node uri = presences.item(i1).getAttributes().getNamedItem("uri");
            if (uri == null) {
                continue;
            }
            FetionFriend ff = MiscUtil.getFriend(uri.getNodeValue(), buddies);
            if (ff == null) {
                continue;
            }
            Element presenceElement = (Element) presences.item(i1);
            if (presenceElement != null) {
                NodeList basicNodes = presenceElement.getElementsByTagName("basic");
                if (basicNodes.getLength() > 0) {
                    Node basicValue = basicNodes.item(0).getAttributes().getNamedItem("value");
                    if (basicValue != null) {
                        Node desc = basicNodes.item(0).getAttributes().getNamedItem("desc");
                        String s = desc == null ? null : desc.getNodeValue();
                        if (s.equals("")) s = null;
                        int oldstatus = ff.getOnlineStatusInt();
                        ff.setOnlineStatus(Integer.parseInt(basicValue.getNodeValue()), s);
                        if (oldstatus == Settings.STATUS_OFFLINE
                                && ff.getOnlineStatusInt() != Settings.STATUS_OFFLINE) {
// FIXME why i con not add res/raw dir to svn index, fuck svn 
//                            MediaPlayer.create(this.ctx, R.raw.online).start();
                        }
                    }
                }
                NodeList personals = presenceElement.getElementsByTagName("personal");
                if (personals.getLength() > 0) {
                    NamedNodeMap nnm = personals.item(0).getAttributes();
                    Node n = nnm.getNamedItem("nickname");
                    if (n != null && !"".equals(n.getNodeValue())) {
                        ff.setNickName(n.getNodeValue());
                    }
                    n = nnm.getNamedItem("impresa");
                    if (n != null && !"".equals(n.getNodeValue())) {
                        ff.impresa = n.getNodeValue();
                    }
                }
                this.onFriendInfoChanged(ff);
            }
        }
    }

    public boolean received(FetionSIPC sipc) {
        if (sipc.isRequest()) {
            try {
                if (sipc.getRequestCmdLine().cmd.equals("BN")) {// 收到通知
                    if (sipc.GetHeaderValue("N").equals("presence")) {
                        Document dom = parseXmlDocument(sipc.body);

                        if (dom != null) {
                            Element docElement = dom.getDocumentElement();
                            if (docElement != null && docElement.getNodeName().equals("events")) {
                                NodeList event = dom.getDocumentElement().getElementsByTagName("event");
                                for (int i = 0; i < event.getLength(); i++) {
                                    Node type = event.item(i).getAttributes().getNamedItem("type");
                                    if (type != null && type.getNodeValue().equals("PresenceChanged")) {
                                        updateFriendPresence((Element) event.item(i));
                                        return true;
                                    }
                                }
                            }

                        }
                    }
                }
            } catch (DOMException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

}
